<template>
  <div
    class="border-1 border-solid z-10"
    :style="{
      borderColor: isFocus ? 'var(--el-color-primary)' : 'var(--el-border-color)'
    }"
  >
    <!-- 工具栏 -->
    <Toolbar
      :editor="editorRef"
      :editorId="editorId"
      class="border-0 b-b-1 border-solid border-[var(--el-border-color)]"
    />
    <!-- 编辑器 -->
    <Editor
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      :editorId="editorId"
      :style="editorStyle"
      @on-change="handleChange"
      @on-focus="handleFocus"
      @on-blur="handleBlur"
      @on-created="handleCreated"
    />
  </div>
</template>

<script setup lang="ts">
import { PropType } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { i18nChangeLanguage, IDomEditor, IEditorConfig, Boot } from '@wangeditor/editor'
import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus'
import { useLocaleStore } from '@/stores/modules/locale'
import { getAccessToken } from '@/utils/auth'
import { getUploadUrl } from '@/components/UploadFile/src/useUpload'
import { s_file_url } from '@/utils/lizard'
import { generic_headers, response_err_msg } from '@/config/axios'
import { Token_Error } from '@/config/axios/errorCode'
import * as LoginApi from '@/api/base/login'

defineOptions({ name: 'Editor' })

type InsertFnType = (url: string, alt: string, href: string) => void

const localeStore = useLocaleStore()

const currentLocale = computed(() => localeStore.getCurrentLocale)

i18nChangeLanguage(currentLocale.value.lang)

const props = defineProps({
  editorId: propTypes.string.def('wangEditor-1'),
  height: propTypes.oneOfType([Number, String]).def('500px'),
  editorConfig: {
    type: Object as PropType<IEditorConfig>,
    default: () => undefined
  },
  // readonly: propTypes.bool.def(false),
  // 改名为disabled，为了更好的兼容form create(要求modelValue与disabled属性)
  disabled: propTypes.bool.def(false),
  modelValue: propTypes.string.def(''),
  placeholder: propTypes.string.def('请输入内容')
})

const emit = defineEmits(['change', 'update:modelValue', 'fullscreen'])

// 编辑器实例，必须用 shallowRef
const editorRef = shallowRef<IDomEditor>()

const valueHtml = ref('')

watch(
  () => props.modelValue,
  (val: string) => {
    // console.log('检测到modelValue变更: ', val, '此时valueHtml值', unref(valueHtml))
    if (val === unref(valueHtml)) return
    // console.log('更新valueHtml值', val)
    // 这里如果没有nextTick再邮件模板编辑页面，切换模板类型时，editor的内容会有问题
    nextTick(() => {
      valueHtml.value = val
    })
  },
  { immediate: true }
)

watch(
  () => valueHtml.value,
  (val: string) => {
    emit('update:modelValue', val)
  }
)

watch(
  () => props.disabled,
  (disabled: boolean) => {
    if (disabled) {
      editorRef.value?.disable()
    } else {
      editorRef.value?.enable()
    }
  }
)

const handleCreated = (editor: IDomEditor) => {
  editorRef.value = editor
  editor.on('fullScreen', () => {
    emit('fullscreen', true)
  })
  editor.on('unFullScreen', () => {
    emit('fullscreen', false)
  })
}

// 确保"只有一个空段落"的情况下，值为空串
function withEmptyHtml(editor) {
  const { getHtml } = editor
  const newEditor = editor
  newEditor.getHtml = () => {
    if (newEditor.isEmpty()) {
      return ''
    }
    return getHtml()
  }

  return newEditor
}

Boot.registerPlugin(withEmptyHtml)

// 编辑器配置
const editorConfig = computed((): IEditorConfig => {
  return Object.assign(
    {
      placeholder: props.placeholder,
      readOnly: props.disabled,
      customAlert: (s: string, t: string) => {
        switch (t) {
          case 'success':
            ElMessage.success(s)
            break
          case 'info':
            ElMessage.info(s)
            break
          case 'warning':
            ElMessage.warning(s)
            break
          case 'error':
            ElMessage.error(s)
            break
          default:
            ElMessage.info(s)
            break
        }
      },
      autoFocus: false,
      scroll: true,
      MENU_CONF: {
        ['uploadImage']: {
          server: getUploadUrl(),
          // 单个文件的最大体积限制，默认为 2M
          maxFileSize: 5 * 1024 * 1024,
          // 最多可上传几个文件，默认为 100
          maxNumberOfFiles: 10,
          // 选择文件时的类型限制，默认为 ['image/*'] 。如不想限制，则设置为 []
          allowedFileTypes: ['image/*'],

          // 自定义增加 http  header
          headers: {
            Accept: '*',
            ...generic_headers()
          },

          // 超时时间，默认为 10 秒
          timeout: 10 * 1000, // 10 秒

          // form-data fieldName，后端接口参数名称，默认值wangeditor-uploaded-image
          fieldName: 'file',

          // 上传之前触发
          onBeforeUpload(file: File) {
            // console.log(file)
            return file
          },
          // 上传进度的回调函数
          onProgress(progress: number) {
            // progress 是 0-100 的数字
            console.log('progress', progress)
          },
          onSuccess(file: File, res: any) {
            console.log('onSuccess', file, res)
          },
          onFailed(file: File, res: any) {
            ElMessage.error(res.message || res.msg)
            console.log('onFailed', file, res)
          },
          onError(file: File, err: any, res: any) {
            ElMessage.error(err.message)
            console.error('onError', file, err, res)
          },
          // 自定义插入图片
          customInsert(res: any, insertFn: InsertFnType) {
            if (res.code?.startsWith('2')) {
              insertFn(s_file_url(res.data.path), '', '')
            } else if (res.code === Token_Error) {
              LoginApi.checkAuth().then((_res) => {
                const accessToken = getAccessToken()
                editorConfig.value.MENU_CONF!.uploadImage.headers.Authorization = accessToken
                editorConfig.value.MENU_CONF!.uploadVideo.headers.Authorization = accessToken
                ElMessage.error('上传失败，请重试')
              })
            } else {
              ElMessage.error(response_err_msg(res))
            }
          }
        },
        ['uploadVideo']: {
          server: getUploadUrl(),
          // 单个文件的最大体积限制，默认为 10M
          maxFileSize: 10 * 1024 * 1024,
          // 最多可上传几个文件，默认为 100
          maxNumberOfFiles: 10,
          // 选择文件时的类型限制，默认为 ['video/*'] 。如不想限制，则设置为 []
          allowedFileTypes: ['video/*'],

          // 自定义增加 http  header
          headers: {
            Accept: '*',
            ...generic_headers()
          },

          // 超时时间，默认为 30 秒
          timeout: 30 * 1000, // 30 秒

          // form-data fieldName，后端接口参数名称，默认值wangeditor-uploaded-image
          fieldName: 'file',

          // 上传之前触发
          onBeforeUpload(file: File) {
            // console.log(file)
            return file
          },
          // 上传进度的回调函数
          onProgress(progress: number) {
            // progress 是 0-100 的数字
            console.log('progress', progress)
          },
          onSuccess(file: File, res: any) {
            console.log('onSuccess', file, res)
          },
          onFailed(file: File, res: any) {
            ElMessage.error(res.message || res.msg)
            console.log('onFailed', file, res)
          },
          onError(file: File, err: any, res: any) {
            ElMessage.error(err.message)
            console.error('onError', file, err, res)
          },
          // 自定义插入图片
          customInsert(res: any, insertFn: InsertFnType) {
            if (res.code?.startsWith('2')) {
              insertFn(s_file_url(res.data.path), '', '')
            } else if (res.code === Token_Error) {
              LoginApi.checkAuth()
                .then((_res) => {
                  const accessToken = getAccessToken()
                  editorConfig.value.MENU_CONF!.uploadImage.headers.Authorization = accessToken
                  editorConfig.value.MENU_CONF!.uploadVideo.headers.Authorization = accessToken
                  ElMessage.error('上传失败，请重试')
                })
                .catch(() => {
                  ElMessage.error('上传失败，请重试')
                })
            } else {
              ElMessage.error(response_err_msg(res))
            }
          }
        }
      }
      // uploadImgShowBase64: true
    },
    props.editorConfig || {}
  )
})

const editorStyle = computed(() => {
  return {
    height: isNumber(props.height) ? `${props.height}px` : props.height
  }
})

const handleChange = (editor: IDomEditor) => {
  emit('change', editor)
}

const isFocus = ref(false)
const handleFocus = (_editor: IDomEditor) => {
  isFocus.value = true
}

const handleBlur = (_editor: IDomEditor) => {
  isFocus.value = false
}

// 组件销毁时，及时销毁编辑器
onBeforeUnmount(() => {
  const editor = unref(editorRef.value)
  // 销毁，并移除 editor
  editor?.destroy()
})

const getEditorRef = async (): Promise<IDomEditor> => {
  await nextTick()
  return unref(editorRef.value) as IDomEditor
}

defineExpose({
  getEditorRef
})
</script>

<style src="@wangeditor/editor/dist/css/style.css"></style>
